home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2007 December / PCWKCD1207B.iso / Blogowanie poza sfera / Flock 0.9.1.3 stable / flock-0.9.1.3.en-US.win32.exe / flock / components / flockPhotoAPIManager.js < prev    next >
Text File  |  2007-10-12  |  31KB  |  891 lines

  1. //
  2. // BEGIN FLOCK GPL
  3. // 
  4. // Copyright Flock Inc. 2005-2007
  5. // http://flock.com
  6. // 
  7. // This file may be used under the terms of of the
  8. // GNU General Public License Version 2 or later (the "GPL"),
  9. // http://www.gnu.org/licenses/gpl.html
  10. // 
  11. // Software distributed under the License is distributed on an "AS IS" basis,
  12. // WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  13. // for the specific language governing rights and limitations under the
  14. // License.
  15. // 
  16. // END FLOCK GPL
  17. //
  18.  
  19.  
  20. const Cc = Components.classes;
  21. const Ci = Components.interfaces;
  22.  
  23. const FLOCK_PHOTO_API_MANAGER_CID               = Components.ID('{18e27f2e-c99c-4915-9907-1a9fc0780ed6}');
  24.  
  25. const nsISupports                   = Components.interfaces.nsISupports;
  26. const nsIClassInfo                  = Components.interfaces.nsIClassInfo;
  27. const nsIFactory                    = Components.interfaces.nsIFactory;
  28. const nsIProperties                 = Components.interfaces.nsIProperties;
  29. const nsILocalFile                  = Components.interfaces.nsILocalFile;
  30. const nsIFile                       = Components.interfaces.nsIFile;
  31. const nsIIOService                  = Components.interfaces.nsIIOService;
  32. const nsIFileProtocolHandler        = Components.interfaces.nsIFileProtocolHandler;
  33. const nsIPreferenceService          = Components.interfaces.nsIPrefBranch;
  34. const flockIPhotoAPIManager         = Components.interfaces.flockIPhotoAPIManager;
  35. const flockIPhotoAPI                = Components.interfaces.flockIPhotoAPI;
  36. const flockIPhotoPeopleService      = Components.interfaces.flockIPhotoPeopleService;
  37. const flockIPollingService          = Components.interfaces.flockIPollingService;
  38. const flockIMigratable              = Components.interfaces.flockIMigratable;
  39.  
  40. const FLOCK_PHOTO_API_MANAGER_CONTRACTID        = '@flock.com/photo-api-manager;1?';
  41. const FLOCK_PHOTO_CONTRACTID        = '@flock.com/photo;1';
  42. const DIRECTORY_SERVICE_CONTRACTID  = '@mozilla.org/file/directory_service;1';
  43. const LOCAL_FILE_CONTRACTID         = '@mozilla.org/file/local;1';
  44. const PREFERENCES_CONTRACTID        = '@mozilla.org/preferences-service;1';
  45. const IO_SERVICE_CONTRACTID         = '@mozilla.org/network/io-service;1';
  46. const RDFCU_CONTRACTID              = '@mozilla.org/rdf/container-utils;1';
  47. const MYWORLD_SERVICE_CONTRACTID    = '@flock.com/myworld-service;1';
  48.  
  49. const RDF_NS = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
  50. const FLOCK_NS = "http://flock.com/rdf#";
  51. const NC_NS = "http://home.netscape.com/NC-rdf#";
  52.  
  53. /* for Cardinal migration */
  54. const OLD_PEOPLE_PHOTO_RDF_FILE = 'flock_people_photo.rdf';
  55. const OLD_PEOPLE_PHOTO_RDF_FILE_RELIC = 'flock_people_photo_old.rdf';
  56. const LIST_ACCOUNTS_ROOT = 'urn:flock:people:photo:lists:accounts';
  57. const MEDIA_FAVES_ROOT = 'urn:media:favorites';
  58.  
  59. /* 0.9.0 migration */
  60. const DATA_RDF_FILE = 'flock-data.rdf';
  61.  
  62. const RDFS = Components.classes['@mozilla.org/rdf/rdf-service;1']
  63.            .getService (Components.interfaces.nsIRDFService);
  64. const IOS = Components.classes[IO_SERVICE_CONTRACTID]
  65.           .getService(Components.interfaces.nsIIOService);
  66. const RDFCU = Components.classes[RDFCU_CONTRACTID]
  67.             .getService(Components.interfaces.nsIRDFContainerUtils);
  68.  
  69. function flockPhotoAPIManager() {
  70.     this.mName2ServiceMap = {};
  71.     this.mService2NameMap = {};
  72.     this.processingQueue = {};
  73.     this.mPrefService = Components.classes[PREFERENCES_CONTRACTID].getService(nsIPreferenceService);
  74.  
  75.     this.mLogger = Components.classes["@flock.com/logger;1"].createInstance(Components.interfaces.flockILogger);
  76.     this.mLogger.init("photo");
  77.     this.mLogger.info("starting up...");
  78.     
  79.     var catmgr = Components.classes["@mozilla.org/categorymanager;1"]
  80.         .getService (Components.interfaces.nsICategoryManager);
  81.     var enum = catmgr.enumerateCategory("flockIPhotoAPI");
  82.     while(enum.hasMoreElements()) {
  83.         try {
  84.             var obj = enum.getNext();
  85.             var supportsString = obj.QueryInterface(Components.interfaces.nsISupportsCString);
  86.             var shortName = supportsString.toString();
  87.             var cid = catmgr.getCategoryEntry("flockIPhotoAPI", shortName); 
  88.             var svc = Components.classes[cid].getService(Components.interfaces.flockIPhotoAPI);
  89.             this.registerAPI(svc, shortName);
  90.         }
  91.         catch(e) {
  92.         }
  93.     }
  94.  
  95.  
  96.     var inst = this;
  97.     var timer = Components.classes["@mozilla.org/timer;1"].createInstance(Components.interfaces.nsITimer);
  98.     var timerFunc = {
  99.         notify: function(aTimer) {
  100.             inst.updateStates();
  101.         },
  102.     }
  103.     timer.initWithCallback(timerFunc, 5 * 1000, 1);
  104. }
  105.  
  106. //flockIMigratable
  107.  
  108. flockPhotoAPIManager.prototype.__defineGetter__('shortname', function() {
  109.   return "Photo Accounts";
  110. });
  111.  
  112. flockPhotoAPIManager.prototype.needsMigration =
  113. function needsMigration(oldVersion) {
  114.   this.mLogger.info(".needsMigration('" + oldVersion + "')\n");
  115.   
  116.   if (oldVersion.substr(0, 3) == "0.7") { // migration from Cardinal (0.7.x)
  117.     var oldPeoplePhotoFile = Components.classes["@mozilla.org/file/directory_service;1"]
  118.                            .getService(Components.interfaces.nsIProperties)
  119.                            .get("ProfD", Components.interfaces.nsIFile);
  120.     oldPeoplePhotoFile.append(OLD_PEOPLE_PHOTO_RDF_FILE);
  121.  
  122.     if (oldPeoplePhotoFile.exists()) {
  123.       this.mLogger.info("needs migration from 0.7.x\n");
  124.       return true;
  125.     }
  126.     return false;
  127.   } else if (oldVersion.substr(0, 5) == "0.9.0") { // migration from 0.9.0
  128.     this.mLogger.info("needs migration from 0.9.0\n");
  129.     return true;
  130.   } else {
  131.     return false;
  132.   }
  133. }
  134.  
  135. flockPhotoAPIManager.prototype.startMigration =
  136. function startMigration(oldVersion, aFlockMigrationProgressListener) {
  137.   this.mLogger.info(".startMigration('" + oldVersion + "', 'aFlockMigrationProgressListener')\n");
  138.   var ctxt = {
  139.     oldVersion: oldVersion,
  140.     listener: aFlockMigrationProgressListener
  141.   };
  142.  
  143.   if (oldVersion.substr(0, 3) == "0.7") { // migration from Cardinal (0.7.x)
  144.     var oldPeoplePhotoFile = Components.classes["@mozilla.org/file/directory_service;1"]
  145.                                        .getService(Components.interfaces.nsIProperties)
  146.                                        .get("ProfD", Components.interfaces.nsIFile);
  147.     oldPeoplePhotoFile.append(OLD_PEOPLE_PHOTO_RDF_FILE);
  148.  
  149.     ctxt.oldPeoplePhotoFile = oldPeoplePhotoFile;
  150.  
  151.     if (oldPeoplePhotoFile.exists())
  152.       ctxt.listener.onUpdate(0, "Migrating photo people");
  153.  
  154.   } else if (oldVersion.substr(0, 5) == "0.9.0") { // migration from 0.9.0
  155.     ctxt.listener.onUpdate(0, "Migrating media queries");
  156.   }
  157.  
  158.   return { wrappedJSObject: ctxt };
  159. }
  160.  
  161. flockPhotoAPIManager.prototype.finishMigration =
  162. function finishMigration(ctxtWrapper) {
  163. }
  164.  
  165. flockPhotoAPIManager.prototype.doMigrationWork =
  166. function doMigrationWork(ctxtWrapper) {
  167.   var ctxt = ctxtWrapper.wrappedJSObject;
  168.   var oldVersion = ctxt.oldVersion;
  169.  
  170.   if (oldVersion.substr(0, 3) == "0.7") {
  171.     if (!ctxt.oldPeoplePhotoFile.exists()) {
  172.       return false;
  173.     }
  174.  
  175.     if (!ctxt.photoMigrator) {
  176.       ctxt.photoMigrator = this._migratePhotoPeople(ctxt);
  177.     }
  178.     if (ctxt.photoMigrator.next()) {
  179.       ctxt.photoMigrator = null;
  180.     }
  181.  
  182.     return Boolean(ctxt.photoMigrator);
  183.   } else if (oldVersion.substr(0, 5) == "0.9.0") {
  184.     this._migrateMediaQueries();
  185.     return false;
  186.   }
  187. }
  188.  
  189. // josh changed latestSeq property of mediaqueries from
  190. // an int to a string - we need to migrate this type change
  191. flockPhotoAPIManager.prototype._migrateMediaQueries =
  192. function _migrateMediaQueries()
  193. {
  194.   this.mLogger.info("._migrateMediaQueries()\n");
  195.   var dataFile = Components.classes["@mozilla.org/file/directory_service;1"]
  196.                                      .getService(Components.interfaces.nsIProperties)
  197.                                      .get("ProfD", Components.interfaces.nsIFile);
  198.   dataFile.append(DATA_RDF_FILE);
  199.   var dataFileDS = RDFS.GetDataSourceBlocking(IOS.newFileURI(dataFile).spec);
  200.  
  201.   // get all media query rdf nodes
  202.   var mqResource = RDFS.GetResource(FLOCK_NS+"CoopType");
  203.   var mqLiteral = RDFS.GetResource(NC_NS+"MediaQuery");
  204.   var mediaQueries = dataFileDS.GetSources(mqResource, mqLiteral, true);
  205.  
  206.   var getTarget = function (aSource, aArcName) {
  207.     var target = dataFileDS.GetTarget(aSource, RDFS.GetResource(FLOCK_NS+aArcName), true);
  208.     target instanceof Components.interfaces.nsIRDFInt;
  209.     return target;
  210.   };
  211.   var getTargetLiteral = function photoAPIManager_getTargetLiteral(aSource, aArcName) {
  212.     var target = dataFileDS.GetTarget(aSource,
  213.                                       RDFS.GetResource(FLOCK_NS+aArcName),
  214.                                       true);
  215.     target instanceof Components.interfaces.nsIRDFLiteral;
  216.     return target;
  217.   };
  218.  
  219.   while (mediaQueries && mediaQueries.hasMoreElements()) {
  220.     var mediaQuery = mediaQueries.getNext();
  221.     mediaQuery.QueryInterface(Components.interfaces.nsIRDFResource);
  222.     var latestSeq = getTarget(mediaQuery, "latestSeq");
  223.     var subject = RDFS.GetResource(mediaQuery.Value);
  224.     var predicate = RDFS.GetResource(FLOCK_NS+"latestSeq");
  225.     var latestSeqString = RDFS.GetLiteral(latestSeq.Value);                
  226.     // replace old int value with string
  227.     dataFileDS.Change(subject, predicate, latestSeq, latestSeqString);
  228.  
  229.     var isPollable = getTarget(mediaQuery, "isPollable");
  230.  
  231.     if (isPollable.Value == "true") {
  232.       // Force refresh on media query
  233.       dataFileDS.Change(subject,
  234.                         RDFS.GetResource(FLOCK_NS + "nextRefresh"),
  235.                         getTargetLiteral(mediaQuery, "nextRefresh"),
  236.                         RDFS.GetLiteral(new Date()));
  237.     }
  238.   }
  239. }
  240.  
  241. flockPhotoAPIManager.prototype._migratePhotoPeople =
  242. function _migratePhotoPeople(ctxt)
  243. {
  244.   dump("PHAN: flockPhotoAPIManager._migratePhotoPeople()\n");
  245.   // Load old datasource
  246.   var oldPhotoPeepsDS = RDFS.GetDataSourceBlocking(IOS.newFileURI(ctxt.oldPeoplePhotoFile).spec);
  247.   var acctsRoot = RDFS.GetResource(LIST_ACCOUNTS_ROOT);
  248.   var streamsRoot = RDFS.GetResource("urn:flock:people:photo:lists:watched");
  249.   var getValue = function (aSource, aArcName) {
  250.     var target = oldPhotoPeepsDS.GetTarget(aSource, RDFS.GetResource(FLOCK_NS+aArcName), true);
  251.     target instanceof Components.interfaces.nsIRDFLiteral;
  252.     return target.Value;
  253.   };
  254.  
  255.   // Migrate accounts
  256.   var accounts = RDFCU.MakeSeq(oldPhotoPeepsDS, acctsRoot).GetElements(); 
  257.   while (accounts && accounts.hasMoreElements()) {
  258.     var account = accounts.getNext();
  259.     account.QueryInterface(Components.interfaces.nsIRDFResource);
  260.     var username = getValue(account, "username");
  261.     var apiShortName = getValue(account, "apiShortName");
  262.     var id = getValue(account, "id");
  263.     var api = this.getAPIFromShortname(apiShortName);
  264.     api.migrateAccount(id, username);
  265.   }
  266.  
  267.   // Load new datasource
  268.   var _coop = Components.classes["@flock.com/singleton;1"]
  269.                         .getService(Components.interfaces.flockISingleton)
  270.                         .getSingleton("chrome://browser/content/flock/common/load-faves-coop.js")
  271.                         .wrappedJSObject;
  272.   var mediaFavesURN = MEDIA_FAVES_ROOT;
  273.   var mediaFaves = _coop.get(mediaFavesURN);
  274.   if (!mediaFaves) {
  275.     mediaFaves = new _coop.Folder(mediaFavesURN);
  276.     _coop.favorites_root.children.add(mediaFaves);
  277.   }
  278.  
  279.   // Migrate favorite photo streams
  280.   var streams = RDFCU.MakeSeq(oldPhotoPeepsDS, streamsRoot).GetElements();
  281.   while (streams && streams.hasMoreElements()) {
  282.     var stream = streams.getNext();
  283.     stream.QueryInterface(Components.interfaces.nsIRDFResource);
  284.     var apiShortName = getValue(stream, "apiShortName");
  285.     var id = getValue(stream, "id");
  286.     var mediaqueryURN = MEDIA_FAVES_ROOT+apiShortName+":user:"+id+"|username:"+getValue(stream, "username");
  287.     var mediaquery = new _coop.MediaQuery(
  288.       mediaqueryURN,
  289.       {
  290.         serviceId: FLOCK_PHOTO_API_MANAGER_CONTRACTID,
  291.         service: apiShortName,
  292.         favicon: this.getAPIFromShortname(apiShortName).iconUrl,
  293.         query: "user:"+id+"|username:"+getValue(stream, "username"),
  294.         name: getValue(stream, "username"),
  295.         isPollable: true
  296.       }
  297.     );
  298.     mediaFaves.children.add(mediaquery);
  299.   }
  300.  
  301.   // rename the old people photo file
  302.   ctxt.oldPeoplePhotoFile.moveTo(null, OLD_PEOPLE_PHOTO_RDF_FILE_RELIC);
  303.   yield true;
  304. }
  305.  
  306. //////////////////////////////////////////////////////////////////////////////
  307. // Implementation
  308. //////////////////////////////////////////////////////////////////////////////
  309.  
  310. //////////////////////////////////////////////////////////////////////////////
  311.  
  312. flockPhotoAPIManager.prototype.mStates = {};
  313. flockPhotoAPIManager.prototype.mListeners = new Array();
  314.  
  315. flockPhotoAPIManager.prototype.addListener = function(aListener) {
  316.     if (aListener == null) {
  317.         throw  "Listener is null!!";
  318.     }
  319.     this.mListeners.push(aListener);
  320. }
  321.  
  322. flockPhotoAPIManager.prototype.updateStates = function() {
  323.     for(var p in this.mStates) {
  324.         var shortName = p;
  325.         var state = this.mStates[p];
  326.         var api = this.getAPIFromShortname(p);
  327.         var newState = api.authState;
  328.         if(newState != state) {
  329.             this.notify(api);
  330.         }
  331.         this.mStates[p] = newState;
  332.     }
  333. }
  334.  
  335. flockPhotoAPIManager.prototype.notify = function(aService) {
  336.     for(var i=0;i<this.mListeners.length;++i) {
  337.             this.mListeners[i].onAPIStateChange(aService);
  338.     }
  339. }
  340.  
  341. flockPhotoAPIManager.prototype.doLogin = function(aAccountURN) {
  342.     // TODO: MAYBE FIXME (CDC):
  343.     // I changed the param for this function from aShortname to aAccountURN
  344.     // ... but I don't think this is actually called from anywhere so for now
  345.     // I'm gonna just throw NOT_IMPLEMENTED instead of fixing it to handle the
  346.     // new parameter.  If it breaks things, I owe the first person who notices
  347.     // it a bubble tea.
  348.     throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  349.  
  350.     var inst = this;
  351.     var myListener = {
  352.         onAuth: function() {
  353.             inst.notify(service);
  354.             inst.setDefaultService(service.shortName);
  355.         },
  356.         onError: function(aError) {
  357.            for (var i=0; i < inst.mListeners.length; i++) {
  358.                aError.QueryInterface(Components.interfaces.flockIError);
  359.                try {
  360.                     var theError = {};
  361.                     theError.errorCode = aError.errorCode;
  362.                     theError.serviceErrorCode = aError.serviceErrorCode;
  363.                     theError.serviceErrorString = aError.serviceErrorString;
  364.                     theError.errorString = aError.errorString;
  365.                     inst.mListeners[i].onError(theError);
  366.                } catch (ex) {
  367.                     this.mLogger.error(ex);
  368.                }
  369.             }
  370.             
  371.         },
  372.     }
  373.     var service = this.getAPIFromShortname(aShortname);
  374.     service.login(null, myListener);
  375.     this.notify(service);
  376. }
  377.  
  378. flockPhotoAPIManager.prototype.doLogout = function(aShortname) {
  379.     var service = this.getAPIFromShortname(aShortname);
  380.     service.logout();
  381.  
  382.     // TODO - maybe change the default service to something that is enabled ???
  383.     
  384.     this.notify(service);
  385. }
  386.  
  387.  
  388. flockPhotoAPIManager.prototype.removeListener = function(aListener) {
  389.     for(var i=0;i<this.mListeners.length;++i) {
  390.         if(aListener==this.mListeners[i]) {
  391.             this.mListeners.splice(i,1);
  392.             break;
  393.         }
  394.     }
  395. }
  396.  
  397. flockPhotoAPIManager.prototype.registerAPI = function(aService, aShortname) {
  398.     
  399.     if (this.mPrefService.getPrefType('flock.photo.service.' + aShortname + '.isDisabled')) {
  400.         if (this.mPrefService.getBoolPref('flock.photo.service.' + aShortname + '.isDisabled')) {
  401.             return true;
  402.         }
  403.         this.mPrefService.setBoolPref('flock.photo.service.' + aShortname + '.isDisabled', false);
  404.     }        
  405.     this.mName2ServiceMap[aShortname] = aService;
  406.     this.mService2NameMap[aService] = aShortname;
  407.     this.mStates[aShortname] = aService.authState;
  408. }
  409.  
  410. flockPhotoAPIManager.prototype.isPreferredService = function (aShortname) {
  411.     return this.mPreferredServices[aShortname];
  412. }
  413.  
  414. flockPhotoAPIManager.prototype.getAPIFromShortname = function(aShortname) {
  415.   // JMC - Special case, me!
  416.     if (aShortname == 'preview') return this;
  417.     return this.mName2ServiceMap[aShortname];
  418. }
  419.  
  420.  
  421. function loadLibraryFromSpec(aSpec)
  422. {
  423.   var loader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
  424.                          .getService(Components.interfaces.mozIJSSubScriptLoader);
  425.   loader.loadSubScript(aSpec);
  426. }
  427.  
  428. loadLibraryFromSpec("chrome://browser/content/flock/common/aggregate.js");
  429.  
  430. flockPhotoAPIManager.prototype.supportsSearch = function (aQuery) {
  431.   return false;
  432. }
  433.  
  434. flockPhotoAPIManager.prototype.search = function (aListener, aQuery, aCount, aPage) {
  435.   // JMC - PQ of all mediaquery objects with new stuff
  436.   var faves_coop = Components.classes['@flock.com/singleton;1']
  437.                              .getService(Components.interfaces.flockISingleton)
  438.                              .getSingleton('chrome://browser/content/flock/common/load-faves-coop.js')
  439.                              .wrappedJSObject;
  440.                              
  441.   // JMC - If it's a page =1 request, perform the search
  442.   // Otherwise, return the next batch from the cached results
  443.   if (aPage > 1) {
  444.     aListener.onSearchResult(this.myUnseenEnum);
  445.   } else {                            
  446.     var mediaQueries = faves_coop.MediaQuery.find({hasUnseenItems: true});
  447.     var streams = [];
  448.     for (x in mediaQueries) {
  449.       var mediaquery = mediaQueries[x];
  450.       streams.push(mediaquery.children.enumerate());
  451.     }
  452.     var aggUnSeen = filterStream( aggregatePhotoStreams( streams ), function(item) {
  453.          return item && item.unseen;
  454.        });
  455.     var inst = this;   
  456.     this.myUnseenEnum = {
  457.       hasMoreElements : function() {
  458.           return this.mEnum.hasMoreElements();
  459.       },
  460.       getNext : function() {
  461.         var child = this.mEnum.getNext();
  462.         // JMC - Get the real flockIPhoto from the coop obj.
  463.         var svcName = child.getParent().service;
  464.         return inst.getAPIFromShortname(svcName).getPhotoFromRDFNode(child.id());
  465.       },
  466.     };
  467.     this.myUnseenEnum.mEnum = aggUnSeen;   
  468.     aListener.onSearchResult(this.myUnseenEnum);
  469.   }
  470. }
  471.  
  472. flockPhotoAPIManager.prototype.__defineGetter__('services', function () { 
  473.     var ar = new Array();
  474.     for(var p in this.mName2ServiceMap) {
  475.         ar.push(this.mName2ServiceMap[p]);
  476.     }
  477.     var rval = {
  478.         getNext: function() {
  479.             var rval = ar.shift();
  480.             return rval;
  481.         },
  482.         hasMoreElements: function() {
  483.             return (ar.length>0);
  484.         },
  485.     }
  486.     return rval;
  487. })
  488.  
  489. flockPhotoAPIManager.prototype.getPreferredServices = function() {
  490.     try {
  491.         var pref = this.mPrefService.getCharPref("flock.photo.preferredServices");
  492.         var ar = pref.split(",");
  493.         var rval = [];
  494.         for(var i=0;i<ar.length;++i) {
  495.             if(ar[i].length) rval.push(ar[i]); 
  496.         }
  497.         return rval;
  498.     } catch(e) {
  499.         //debug(e);
  500.         debug("getPreferredServices: No preferred services found, returning an empty set\n");
  501.         return [];
  502.     }
  503. }
  504.  
  505. flockPhotoAPIManager.prototype.setPreferredServices = function(aArray) {
  506.     var val = aArray.join(",");
  507.     var pref = this.mPrefService.setCharPref("flock.photo.preferredServices", val);
  508. }
  509.  
  510. flockPhotoAPIManager.prototype.getDefaultService = function() {
  511.     try {
  512.         var pref = this.mPrefService.getCharPref("flock.photo.lastService");
  513.         if (pref && pref.length)
  514.             return this.getAPIFromShortname(pref);
  515.     } catch(e) {
  516.         return null;
  517.     }
  518. }
  519.  
  520. flockPhotoAPIManager.prototype.setDefaultService = function(aShortname) {
  521.     try {
  522.         this.mPrefService.setCharPref("flock.photo.lastService", aShortname);
  523.     } catch(e) {
  524.         debug(e);
  525.     }
  526. }
  527.  
  528. flockPhotoAPIManager.prototype.__defineGetter__('preferredServices', function () { 
  529. try {
  530.     var inst = this;
  531.     var ar = this.getPreferredServices();
  532.     var rval = {
  533.         getNext: function() {
  534.             var shortName = ar.shift();
  535.             var api = inst.getAPIFromShortname(shortName);
  536.             return api;
  537.         },
  538.         hasMoreElements: function() {
  539.             return (ar.length>0);
  540.         },
  541.     }
  542.     return rval;
  543.     } catch(e) {
  544.         debug(e);
  545.     }
  546. })
  547.  
  548.  
  549. flockPhotoAPIManager.prototype.__defineGetter__("hasNewMedia", function () {
  550.   var faves_coop = Components.classes['@flock.com/singleton;1']
  551.                              .getService(Components.interfaces.flockISingleton)
  552.                              .getSingleton('chrome://browser/content/flock/common/load-faves-coop.js')
  553.                              .wrappedJSObject;
  554.   var mediaFavsFolder = faves_coop.get(MEDIA_FAVES_ROOT);
  555.   var favItems = mediaFavsFolder.children.enumerate();
  556.   if (favItems) {
  557.     while (favItems.hasMoreElements()) {
  558.        var favItem = favItems.getNext();
  559.        if (favItem && favItem.hasUnseenItems) return true;
  560.     }
  561.   } else {
  562.     return false;
  563.   }
  564.   return false;
  565. })
  566.  
  567. // This method has three responsibilities,
  568. // and two entry points.
  569. // First, it ensures that the mediaquery 
  570. // contains the most recent 5 RichPhoto items.
  571. // Second, it ensures that RichPhoto items that are being
  572. // Freshly created are marked as "unseen".
  573. // Finally, it does NOT mark them as unseen on first refresh.
  574.  
  575. // The enum is required to be sorted, newest first
  576.  
  577. flockPhotoAPIManager.prototype.processPhotoStream =
  578. function photoAPI_processPhotoStream(aUrn, aEnum, bMarkUnseen)
  579. {
  580.   var MAX_MEDIAQUERY_CHILDREN = 5;
  581.   var faves_coop = Components.classes['@flock.com/singleton;1']
  582.                               .getService(Components.interfaces.flockISingleton)
  583.                               .getSingleton('chrome://browser/content/flock/common/load-faves-coop.js')
  584.                               .wrappedJSObject;
  585.                               
  586.   var myStream = faves_coop.get(aUrn);
  587.   if (!myStream || this.processingQueue[aUrn]) {
  588.     return; 
  589.   }  
  590.   this.processingQueue[aUrn] = true;
  591.   
  592.   // JMC - Pruning run, save to destroy children later
  593.   var myStreamEnum = myStream.children.enumerateBackwards();
  594.   var oldChildren = [];
  595.   while (myStreamEnum.hasMoreElements())
  596.   {
  597.     var child = myStreamEnum.getNext();
  598.     if (child) {
  599.       oldChildren.push(child);
  600.       myStream.children.remove(child);
  601.     }
  602.   }
  603.  
  604.   var biggestSeq = myStream.latestSeq;
  605.   var latestDate = myStream.latestDate;
  606.   var inst = this;
  607.   var photoIndex = 1;
  608.   var newContent = false;
  609.   function myWorker(shouldYield) {
  610.     while (aEnum.hasMoreElements() && photoIndex <= MAX_MEDIAQUERY_CHILDREN) {
  611.       var photo = aEnum.getNext();
  612.       photo.QueryInterface(Components.interfaces.flockIPhoto);
  613.       
  614.       // For streams with no uploadDate, we use now. Is that good?
  615.       var photoDate = (photo.uploadDate) ? new Date(parseInt(photo.uploadDate)) : new Date();
  616.       if (photoDate > latestDate) {
  617.         latestDate = photoDate;
  618.         biggestSeq = photo.id;
  619.       }
  620.  
  621.       var urn = 'urn:flock:item:photo:'+myStream.service+':'+photo.id;
  622.       var photoFav;
  623.       photoFav = faves_coop.get(urn);
  624.       var alreadyIn = (photoFav != null);
  625.       if (!alreadyIn) {
  626.         newContent = true;
  627.       }
  628.         
  629.       // if it doesn't exist, create it
  630.       if (!photoFav) {
  631.         photoFav = new faves_coop.RichPhoto(urn, {
  632.           datevalue: photoDate,
  633.           URL: photo.webPageUrl,
  634.           thumbnail: photo.thumbnail,
  635.           cachedThumbnail: "",
  636.           is_public: photo.is_public,
  637.           is_video: photo.is_video,
  638.           midSizePhoto: photo.midSizePhoto,
  639.           name: photo.title,
  640.           username: photo.username,
  641.           userid: photo.userid,
  642.           photoid: photo.id,
  643.           largeSizePhoto: photo.largeSizePhoto
  644.         });
  645.         // add new photostream to myworld service queue
  646.         var myworldService = Cc[MYWORLD_SERVICE_CONTRACTID].getService(Ci.flockIMyworldService);
  647.         myworldService.queueResource(urn);
  648.       }
  649.  
  650.       myStream.children.addOnce(photoFav);
  651.       
  652.       if (!myStream.firstRefresh && !alreadyIn && bMarkUnseen) {
  653.         photoFav.unseen = true;
  654.       }
  655.       photoIndex++;
  656.       if (shouldYield()) yield;
  657.     }
  658.     
  659.     // if the old children aren't new children, and they're orphans, then destroy them
  660.     for(var i =0; i < oldChildren.length; i++)
  661.     {
  662.       var child = oldChildren[i];
  663.       if (child.getParents().length < 1)
  664.       {
  665.         child.destroy();
  666.       }
  667.     }
  668.     // If we added a newer photo
  669.     if (newContent) {
  670.       myStream.latestSeq = biggestSeq;
  671.       myStream.latestDate = latestDate;
  672.       if (!myStream.firstRefresh && bMarkUnseen) {
  673.         // JMC - Shouldn't this propagate?
  674.         myStream.hasUnseenItems = true;
  675.         var parent = myStream.getParent();
  676.         if (parent)
  677.         {
  678.           parent.hasUnseenItems = true;
  679.         }
  680.       }
  681.     }
  682.     myStream.firstRefresh = false;                         
  683.     if (myStream.latestDate != latestDate) {
  684.       myStream.latestDate = latestDate;
  685.     }
  686.  
  687.     inst.processingQueue[aUrn] = false;
  688.   }
  689.   schedule(0.3, 30, myWorker);
  690. }
  691.  
  692. flockPhotoAPIManager.prototype.refresh = function(aUrn, aListener) {
  693.  
  694.   var faves_coop = Components.classes['@flock.com/singleton;1']
  695.                              .getService(Components.interfaces.flockISingleton)
  696.                              .getSingleton('chrome://browser/content/flock/common/load-faves-coop.js')
  697.                              .wrappedJSObject;
  698.   var photoAPIManager = this;
  699.   var listener = function(aStream, aSearchListener) {
  700.     this.mStream = aStream;
  701.     this.mListener = aSearchListener;
  702.   }
  703.   
  704.   var myStream = faves_coop.get(aUrn);
  705.   if (!myStream) {
  706.     aListener.onError();
  707.     return; 
  708.   }
  709.  
  710.   listener.prototype.onSearchResult = function(aEnum) {
  711.     photoAPIManager.processPhotoStream(aUrn, aEnum, true);
  712.     this.mListener.onResult(null, null);
  713.   };
  714.  
  715.   listener.prototype.onError = function(aError) {
  716.       photoAPIManager.mLogger.error(aError);
  717.       if(this.mListener) this.mListener.onError(aError);
  718.   };
  719.  
  720.   var theApi = this.getAPIFromShortname(myStream.service);
  721.   if (!theApi) {
  722.     aListener.onResult(null, null);
  723.     return; 
  724.   }
  725.   if (theApi.needsAuthenticatedApi && !theApi.isLoggedIn) {
  726.     aListener.onResult(null, null);
  727.     return;
  728.   }
  729.   
  730.   theApi.search(new listener(myStream, aListener), myStream.query, 5, null);
  731.   return;
  732.  
  733. }
  734.  
  735.  
  736. flockPhotoAPIManager.prototype.flags = nsIClassInfo.SINGLETON;
  737. flockPhotoAPIManager.prototype.classDescription = "Flock Photo People Service";
  738. flockPhotoAPIManager.prototype.getInterfaces = function (count) {
  739.     var interfaceList = [Components.interfaces.flockISocialWebService, flockIPhotoAPIManager, flockIPollingService,
  740.                          flockIMigratable, nsIClassInfo];
  741.     count.value = interfaceList.length;
  742.     return interfaceList;
  743. }
  744.  
  745. flockPhotoAPIManager.prototype.getHelperForLanguage = function (count) {return null;}
  746.  
  747. // JMC - Making this a social photo service for the preview stream
  748. flockPhotoAPIManager.prototype.QueryInterface =
  749. function (iid) {
  750.     if (!iid.equals(Components.interfaces.flockISocialWebService) &&
  751.         !iid.equals(flockIPhotoAPIManager) && 
  752.         !iid.equals(flockIPollingService) &&
  753.         !iid.equals(flockIMigratable) &&
  754.         !iid.equals(nsIClassInfo) &&
  755.         !iid.equals(nsISupports))
  756.         throw Components.results.NS_ERROR_NO_INTERFACE;
  757.     return this;
  758. }
  759.  
  760. // Module implementation
  761. var PhotoPeopleModule = new Object();
  762.  
  763. PhotoPeopleModule.registerSelf =
  764. function (compMgr, fileSpec, location, type)
  765. {
  766.     compMgr = compMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);
  767.  
  768.     compMgr.registerFactoryLocation(FLOCK_PHOTO_API_MANAGER_CID, 
  769.                                     "Flock Photo API Manager JS Component",
  770.                                     FLOCK_PHOTO_API_MANAGER_CONTRACTID, 
  771.                                     fileSpec, 
  772.                                     location,
  773.                                     type);
  774.     var catman = Components.classes['@mozilla.org/categorymanager;1']
  775.       .getService(Components.interfaces.nsICategoryManager);
  776.      
  777.     catman.addCategoryEntry('flockMigratable', 'photoservice', FLOCK_PHOTO_API_MANAGER_CONTRACTID, true, true);
  778. }
  779.  
  780. PhotoPeopleModule.getClassObject =
  781. function (compMgr, cid, iid) {
  782.     if (!cid.equals(FLOCK_PHOTO_API_MANAGER_CID))
  783.         throw Components.results.NS_ERROR_NO_INTERFACE;
  784.     
  785.     if (!iid.equals(Components.interfaces.nsIFactory))
  786.         throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  787.     
  788.     return PhotoAPIManagerFactory;
  789. }
  790.  
  791. PhotoPeopleModule.canUnload =
  792. function(compMgr)
  793. {
  794.     return true;
  795. }
  796.     
  797. /* factory object */
  798. var PhotoAPIManagerFactory = new Object();
  799.  
  800. PhotoAPIManagerFactory.createInstance =
  801. function (outer, iid) {
  802.     if (outer != null)
  803.         throw Components.results.NS_ERROR_NO_AGGREGATION;
  804.  
  805.     return (new flockPhotoAPIManager()).QueryInterface(iid);
  806. }
  807.  
  808. /* entrypoint */
  809. function NSGetModule(compMgr, fileSpec) {
  810.     return PhotoPeopleModule;
  811. }
  812.  
  813.  
  814.  
  815. // a utility for async scheduling:
  816.  
  817. // cheap scheduler for javascript tasks
  818.  
  819. // aSlice is the maximum timeslice in seconds. 0.5 or less is a good value
  820.  
  821. // aPercent is the percent of wall-clock time that should be spent executing
  822. // the task - eg: 20 = one fifth of the time
  823.  
  824. // aTask is defined to be a generator taking an function that evaluates
  825. // to true if it should yield like:
  826. // function task (should_yield) {
  827. //   while (some_condition) {
  828. //     do some work...
  829. //     if (should_yield()) yield;
  830. //   }
  831. // }
  832. // NOTE: should_yield() is fairly cheap but in a tight inner loop it might
  833. //       make sense to call it every N cycles
  834.  
  835. var timers = [];
  836. function schedule (aSlice, aPercent, aTask) {
  837.   // convert percent to ratio
  838.   var ratio = aPercent / 100.0;
  839.  
  840.   // convert slice to milliseconds
  841.   var slice = aSlice * 1000.0;
  842.  
  843.   function milliseconds () { return (new Date ()).getTime (); }
  844.   var wall_start = milliseconds ();
  845.   var process_time = 0;
  846.  
  847.    
  848.   var process_start = 0;
  849.   function should_yield () {
  850.     var now = milliseconds ();
  851.     var so_far = now - process_start;
  852.     var wall_time = now - wall_start;
  853.     return so_far >= slice || ((wall_time * ratio) < (process_time + so_far));
  854.   }
  855.  
  856.   var generator = aTask (should_yield);
  857.  
  858.   var timer = Components.classes['@mozilla.org/timer;1']
  859.     .createInstance (Components.interfaces.nsITimer);
  860.   var callback = {
  861.     notify: function () {
  862.       var wall_time = milliseconds () - wall_start;
  863.  
  864.       if ((wall_time * ratio) < process_time) {
  865.         // if we've spent more time than we're supposed to
  866.         // don't run this cycle
  867.       }
  868.  
  869.       process_start = milliseconds ();
  870.       try {
  871.         generator.next ();
  872.       } catch (err if err instanceof StopIteration) {
  873.         // the task is complete
  874.         timer.cancel ();
  875.         for (var i=0; i<timers.length; i++) { 
  876.           if (timers[i] == timer) {
  877.             timers.splice (i,1);
  878.           }
  879.         }
  880.         delete timer;
  881.       }
  882.       process_time += (milliseconds () - process_start);
  883.     }
  884.   }
  885.  
  886.   timer.initWithCallback (callback, Math.round (slice/ratio),
  887.       Components.interfaces.nsITimer.TYPE_REPEATING_SLACK)
  888.   timers.push (timer);
  889. }
  890.  
  891.